//============================================================================
// Copyright (C) 2011-2013 Brett R. Jones
// All Rights Reserved
//
// You may redistribute and/or modify for non commercial and commercial uses
// provided this copyright notice remains in place and is not modified
//
// This code is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// brettjones1900@gmail.com
// http://www.p2panarchy.com
//============================================================================

#include "SharedFilesMgr.h"

#ifdef USE_PLUGIN_FILE_SERVER

#include "SharedDirInfo.h"
#include <VxUtilLib/VxFileUtil.h>


static U32 UpdateSharedFilesThreadFunc( void * pvContext );

//============================================================================
SharedFilesMgr::SharedFilesMgr()
: m_s64TotalByteCnt(0)
, m_u16FileTypes(0)
{
}

//============================================================================
SharedFilesMgr::~SharedFilesMgr()
{
	sharedFilesShutdown();
}

//============================================================================
void SharedFilesMgr::sharedFilesStartup()
{
	updateFilesList();
}

//============================================================================
void SharedFilesMgr::sharedFilesShutdown( void )
{
	KillThread();
	m_oFilesListMutex.Lock();
	clearSharedDirList();
	m_oFilesListMutex.Unlock();
}

//============================================================================
void SharedFilesMgr::clearSharedDirList( void )
{
	m_u16FileTypes = 0;
	m_s64TotalByteCnt = 0;

	std::vector<SharedDirInfo*>::iterator oIter;
	for( oIter = m_oSharedList.begin(); oIter != m_oSharedList.end(); ++oIter )
	{
		delete (*oIter);
	}
	m_oSharedList.clear();
}

//============================================================================
void SharedFilesMgr::sharedFilesUpdate(std::vector<std::string>& sharedDirList)
{
	sharedFilesShutdown();

	std::vector<std::string>::iterator oIter;
	for( oIter = sharedDirList.begin(); oIter != sharedDirList.end(); ++oIter )
	{
		m_oSharedList.push_back( new SharedDirInfo(*oIter));
	}

	sharedFilesStartup();
}

//============================================================================
void SharedFilesMgr::updateFilesList( void )
{
	if( false == IsThreadCreated() )
	{
		StartThread( (VX_THREAD_FUNCTION_T)UpdateSharedFilesThreadFunc, this, "UpdateSharedFiles" );
	}
	else
	{
		log_msg( LOG_ERROR, "SharedFilesMgr::updateFilesList: Thread Still Running\n" );
	}
}

//============================================================================
void SharedFilesMgr::updateFileTypes( void )
{
	m_s64TotalByteCnt = 0;
	m_u16FileTypes = 0;
	std::vector<SharedDirInfo*>::iterator oIter;
	for( oIter = m_oSharedList.begin(); oIter != m_oSharedList.end(); ++oIter )
	{
		m_u16FileTypes |= (*oIter)->getSharedFileTypes();
		m_s64TotalByteCnt += (*oIter)->m_s64TotalByteCnt;
	}
}

//============================================================================
bool SharedFilesMgr::localFileNameToRemoteFileName( std::string& strLocalFileName, std::string& strRetRemoteFileName )
{
	std::vector<SharedDirInfo*>::iterator oIter;
	for( oIter = m_oSharedList.begin(); oIter != m_oSharedList.end(); ++oIter )
	{
		if( (*oIter)->localFileNameToRemoteFileName( strLocalFileName, strRetRemoteFileName ) )
		{
			return true;
		}
	}
	return false;
}

//============================================================================
bool SharedFilesMgr::remoteFileNameToLocalFileName( std::string& strRemoteFileName, std::string& strRetLocalFileName )
{
	std::vector<SharedDirInfo*>::iterator oIter;
	for( oIter = m_oSharedList.begin(); oIter != m_oSharedList.end(); ++oIter )
	{
		if( (*oIter)->remoteFileNameToLocalFileName( strRemoteFileName, strRetLocalFileName ) )
		{
			return true;
		}
	}
	return false;
}

//============================================================================
static U32 UpdateSharedFilesThreadFunc( void * pvContext )
{
	VxThread * poThread = (VxThread *)pvContext;
	poThread->SetIsThreadRunning( true );
	SharedFilesMgr * poMgr = (SharedFilesMgr *)poThread->GetUserParam();

	poMgr->m_oFilesListMutex.Lock();

	std::vector<SharedDirInfo*>::iterator oIter;
	for( oIter = poMgr->m_oSharedList.begin(); oIter != poMgr->m_oSharedList.end(); ++oIter )
	{
		if( poThread->isAborted() )
		{
			goto thread_done;
		}
		(*oIter)->updateFileList( poThread );
	}

thread_done:
	poMgr->m_oFilesListMutex.Unlock();
	poMgr->onSharedFilesUpdated();
	//! Thread calls this just before exit
	poThread->ThreadAboutToExit();
	return 0;
}


/*
//============================================================================
RCODE SharedFilesMgr::SendMatchList(	U32			u32SktNum,		// Tronacom socket number
									CString &	csMatchString,	// Search Match name
									U16			u16FileTypes,	// types of files to match
									U16			u16LimitType,	// file size limit type
									U64			u64SizeLimit,	// file size limit
									BOOL		bCloseAfterSend,// if true close the socket after send
									BOOL		bEncrypt )		// if true encrypt
{	
	int i;
	RCODE rc = 0;

	PktShareFileList gPktList;
	char as8FileName[ MAX_PATH ];
	char as8TmpFileName[ MAX_PATH ];
	long s32PktIdx = 0;
	unsigned long u32FileLen = 0;
	BOOL bFileOpen = FALSE;
	FILE * pgFile = 0;
	CStringArray acsMatchTokens;
	BOOL bSendAll = FALSE;
	ShareFileInfo * poInfo;

	if( 1 == csMatchString.GetLength() && 
		'*' == csMatchString[ 0 ] )
	{
		bSendAll = TRUE; //send complete list
	}
	else
	{

		rc = ParseSearchString(	csMatchString,		//String of search tokens
							acsMatchTokens );	//return seperate token in each String
	}
	if( rc )
	{
		log_msg( 0, "SharedFilesMgr::SendMatchList Error 0x%x occured parsing string\n", rc );
		ASSERT( FALSE );
	}
	else
	{
		int	iCnt = m_oList.GetSize();
		for( i = 0; i < iCnt; i++ )
		{
			poInfo = &m_oList[ i ];
			if( 0 == ( u16FileTypes & poInfo->m_u32Flags ) )
			{
				//is not the file type we are searching for
				continue;
			}
			if( bSendAll ||
				Match( acsMatchTokens, poInfo->m_csDisplayName ) )
			{
				if( 0 == poInfo->m_s64FileLen )
				{
					log_msg( 0, "SharedFilesMgr::Zero Length File %s\n", (const char *) poInfo->m_csDisplayName );
					continue;
				}
				//add file to PktFileList
				unsigned char * pu8Temp = poInfo->m_pu8ChoppedDisplayName;
				unsigned long u32ItemLen = * pu8Temp + sizeof( __int64 );
				if( gPktList.m_u16PktLen + u32ItemLen > sizeof( PktShareFileList ) )
				{
					//wont fit in this Pkt so store and start another
					gPktList.m_u16PktLen = (( gPktList.m_u16PktLen + 15 ) & ~15 );
					if( FALSE == bFileOpen )
					{	
						tmpnam( as8TmpFileName );
						strcpy( as8FileName, g_oGlobals.m_csExeDir );
						strcat( as8FileName, "TcFileShareRes" );
						strcat( as8FileName, as8TmpFileName );
						strcat( as8FileName, "tmp" );
						pgFile = fopen( as8FileName, "wb+" );
						if( 0 == pgFile )
						{
							rc = FILE_OPEN_ERR;
							break;
						}
						bFileOpen = TRUE;
					}
					if( (unsigned long)gPktList.m_u16PktLen != fwrite( &gPktList, 
																		1, 
																		(unsigned long)gPktList.m_u16PktLen, 
																		pgFile ) )
					{
						rc = FILE_WRITE_ERR;
					}
					u32FileLen += gPktList.m_u16PktLen;
					gPktList.m_u16PktLen = gPktList.EmptyLen();
					s32PktIdx = 0;
					gPktList.m_u16ListCnt = 0;
				}
				//copy file length first
				*( ( __int64 *)&gPktList.m_au8List[ s32PktIdx ] ) = poInfo->m_s64FileLen;
				//copy chopped string that has display name
				memcpy( &gPktList.m_au8List[ s32PktIdx + sizeof( __int64 ) ], poInfo->m_pu8ChoppedDisplayName, u32ItemLen - sizeof( __int64 ) );

				//show trace of file we added to list
				char as8Buf[ 256 * 2 ];
				VxUnchopStr( poInfo->m_pu8ChoppedDisplayName, as8Buf );
				log_msg( 0, "Added to PktFileList file size %d file %s\n", 
						*( ( unsigned long *)&gPktList.m_au8List[ s32PktIdx ] ),
						as8Buf );


				//increment vars for next item
				s32PktIdx += u32ItemLen;
				gPktList.m_u16ListCnt++;
				gPktList.m_u16PktLen += (unsigned short)u32ItemLen;
			}	
		}
	}
	if( pgFile )
	{
		if( u32FileLen )
		{
			//we allready have at least one pkt in file
			//so add this one if has data
			if( s32PktIdx )
			{
				//we have a pkt with data
				gPktList.m_u16PktLen = (( gPktList.m_u16PktLen + 15 ) & ~15 );
			
				//flush Pkt to file
				if( (unsigned long)gPktList.m_u16PktLen != fwrite( &gPktList, 
																	1, 
																	(unsigned long)gPktList.m_u16PktLen, 
																	pgFile ) )
				{
					rc = FILE_WRITE_ERR;
				}
				u32FileLen += gPktList.m_u16PktLen;
				if( u32FileLen & 0x0f )
				{
					//file length is not on 16 byte boundery so fill in a little
					if( 16 - (u32FileLen & 0x0f) != fwrite( &gPktList, 
																		16 - (u32FileLen & 0x0f),
																		(unsigned long)gPktList.m_u16PktLen, 
																		pgFile ) )
					{
						rc = FILE_WRITE_ERR;
					}
				}
				fclose( pgFile );
			}
			//use threaded file send to send list
			//this uses a separate thread to send file containing packets
			rc = g_poApi->SktSendFile(	u32SktNum,// Socket to send on
							as8FileName,	// packet to be sent via tcp connection
							0,	//offset into file to start at
							4096, //size of pkt chunks
							-2,	// PktType to put in header 	
								// positive value..add header of u16PktType to each chunk sent
								//  -1 = use no headers..send file in chunks of u32PktSize in length
								//	-2 = file is file of packets add value in size of pkt chunks to pkt types
							0, //instance value if has header
							TRUE,		// if true delete file after it is sent
							TRUE,		// if true disconnect after send
							0, //user data for callback
							NULL );		// callback from thread with File Send status
		}
	}
	else
	{
		//we havent even written one pkt to file so
		//just send this one
		gPktList.m_u16PktLen = (( gPktList.m_u16PktLen + 15 ) & ~15 );
 
		g_poApi->SktSend( u32SktNum, &gPktList, bCloseAfterSend );
	}
	return rc;
}

//============================================================================
RCODE SharedFilesMgr::AddDir( CString & csDirPath )
{
	HRESULT hr;
	char as8FullPath[ MAX_PATH ];

	struct _stat gStat;
	int i;


    CArray<CString, CString&> acsWildNameList;//Extensions ( file extentions )
	acsWildNameList.Add( CString( "*.*" ) );
	CArray<SharedFileInfo, SharedFileInfo&> agSharedFilesList;//return FileInfo in array

	VxFindFilesByName(	csDirPath,				//start path to search in
						acsWildNameList,//Extensions ( file extentions )
						agSharedFilesList,//return FileInfo in array
						TRUE,						//recurse subdirectories if TRUE
						FALSE		//if TRUE dont return files matching filter else return files that do
 						);
	int iCnt = agSharedFilesList.GetSize();
	//expand list to include the new files
	int iCurListCnt = m_oList.GetSize();
	m_oList.SetSize( iCnt + iCurListCnt );
	int iResolvedListIdx = iCurListCnt;

	for( i = 0; i < iCnt; i++ )
	{
		CString cs = agSharedFilesList[ i ];
		__int64 s64FileLen = agSharedFilesList[ i ].m_s64Len;
		VxMakeForwardSlashPath( cs );

		//get file extension
		CString csExt = VxGetFileExtension( cs );
		char * pExt = (char *)((const char *)csExt);
		BOOL bIsLink = FALSE;

		if( 0 == strcmp( pExt, "lnk" ) )
		{ 
			bIsLink = TRUE; 
			as8FullPath[ 0 ] = 0;
			hr = VxResolveShortcut(	0,				//handle to window of caller
									cs,	//.lnk file
									as8FullPath );		//return path to target file
			if( FAILED( hr ) )
			{
				TRACE("Unable to resolve path in %s\n", (const char *)cs );
				continue;
			}
			else
			{
				cs = as8FullPath;	
				int iResult = ::_stat( (const char *)cs, &gStat );
				if( iResult != 0 )
				{
					TRACE( "Problem getting file information\n" );
					continue;
				}
				else
				{
					s64FileLen = gStat.st_size;
					csExt = VxGetFileExtension( cs );
					pExt = (char *)((const char *)csExt);
				}
			}
		}
		//weve got a live one so

		unsigned long u32FileFlags = 0;

		if( IsImageFileExtention( pExt ) )
		{
			m_bHasImage = TRUE;
			u32FileFlags |= FILE_TYPE_PHOTO;
			m_u32FileFlags |= FILE_TYPE_PHOTO;
		}
		else if( IsAudioFileExtention( pExt ) )
		{
			m_bHasAudio = TRUE;
			u32FileFlags |= FILE_TYPE_AUDIO;
			m_u32FileFlags |= FILE_TYPE_AUDIO; 
		}
		else if( IsVideoFileExtention( pExt ) )
		{
			m_bHasVideo = TRUE;
			u32FileFlags |= FILE_TYPE_VIDEO;
			m_u32FileFlags |= FILE_TYPE_VIDEO; 
		}
		else if( IsExecutableFileExtention( pExt ) )
		{
			m_bHasExecutable = TRUE;
			u32FileFlags |= FILE_TYPE_EXECUTABLE;
			m_u32FileFlags |= FILE_TYPE_EXECUTABLE;
		}
		else if( IsArchiveOrDocFileExtention( pExt ) )
		{
			m_bHasArchiveOrDoc = TRUE;
			u32FileFlags |= FILE_TYPE_ARCHIVE_OR_DOC;
			m_u32FileFlags |= FILE_TYPE_ARCHIVE_OR_DOC;
		}
		else if( IsArchiveOrDocFileExtention( pExt ) )
		{
			m_bHasCdImage = TRUE;
			u32FileFlags |= FILE_TYPE_CD_IMAGE;
			m_u32FileFlags |= FILE_TYPE_CD_IMAGE;
		}
		else
		{
			m_bHasOther = TRUE;
			u32FileFlags |= FILE_TYPE_OTHER;
			m_u32FileFlags |= FILE_TYPE_OTHER;
		}
		//fill in the data
		m_s64TotalByteCnt += s64FileLen;
		ShareFileInfo * poInfo = &m_oList[ iResolvedListIdx ];
		poInfo->m_csPathAndName = cs;
		poInfo->m_s64FileLen = s64FileLen;
		poInfo->m_u32Flags = u32FileFlags;
		if( bIsLink )
		{
			//display name is the file name
			poInfo->m_csDisplayName = VxGetFileNameFromFullPath( cs );
		}
		else
		{
			//display name is full path - root path
			char * pTemp = (char *)(const char *)cs;
			int iRootLen =  csDirPath.GetLength();
			ASSERT( iRootLen < cs.GetLength() );
			if( iRootLen )
			{
				if( '\\' == pTemp[ iRootLen - 1 ] )
				{
					//root path has the slash
					poInfo->m_csDisplayName = &pTemp[ iRootLen ];
				}
				else
				{
					//root path doesnt has the slash so add 1 to length
					poInfo->m_csDisplayName = &pTemp[ iRootLen + 1 ];
				}

			}
		}

		unsigned char * pu8ChoppedStr = new unsigned char[ poInfo->m_csDisplayName.GetLength() + 10 ];
		VxChopStr( (char *)(const char *) poInfo->m_csDisplayName, pu8ChoppedStr );
		if( poInfo->m_pu8ChoppedDisplayName )
		{
			delete poInfo->m_pu8ChoppedDisplayName;
		}
		poInfo->m_pu8ChoppedDisplayName = pu8ChoppedStr;
		LogChoppedString( pu8ChoppedStr );
		poInfo = &m_oList[0];
		const char * pTemp = (const char *)poInfo->m_csDisplayName;
		
		iResolvedListIdx++;

	}
	m_oList.SetSize( iResolvedListIdx );//remove any unused entries
	return 0;
}
//============================================================================
//! make array of packets with matching files
RCODE SharedFilesMgr::MakeMatchList(	CPtrArray * paoRetListPkts, // return list of packets of file lists
									CString &	csMatchString,	// Search Match name
									U16			u16FileTypes,	// types of files to match
									U16			u16LimitType,	// file size limit type
									U64			u64SizeLimit )	// file size limit
{	
	int i;
	RCODE rc = 0;

	PktShareFileList gPktList;
	long s32PktIdx = 0;
	unsigned long u32FileLen = 0;
	BOOL bFileOpen = FALSE;
	FILE * pgFile = 0;
	CStringArray acsMatchTokens;
	BOOL bSendAll = FALSE;
	ShareFileInfo * poInfo;

	if( 1 == csMatchString.GetLength() && 
		'*' == csMatchString[ 0 ] )
	{
		bSendAll = TRUE; //send complete list
	}
	else
	{

		rc = ParseSearchString(	csMatchString,		//String of search tokens
							acsMatchTokens );	//return seperate token in each String
	}
	if( rc )
	{
		log_msg( 0, "SharedFilesMgr::SendMatchList Error 0x%x occured parsing string\n", rc );
		ASSERT( FALSE );
	}
	else
	{
		int	iCnt = m_oList.GetSize();
		for( i = 0; i < iCnt; i++ )
		{
			poInfo = &m_oList[ i ];
			if( 0 == ( u16FileTypes & poInfo->m_u32Flags ) )
			{
				//is not the file type we are searching for
				continue;
			}
			if( bSendAll ||
				Match( acsMatchTokens, poInfo->m_csDisplayName ) )
			{
				if( 0 == poInfo->m_s64FileLen )
				{
					log_msg( 0, "SharedFilesMgr::Zero Length File %s\n", (const char *) poInfo->m_csDisplayName );
					continue;
				}
				//add file to PktFileList
				unsigned char * pu8Temp = poInfo->m_pu8ChoppedDisplayName;
				unsigned long u32ItemLen = * pu8Temp + sizeof( __int64 );
				if( gPktList.m_u16PktLen + u32ItemLen > sizeof( PktShareFileList ) )
				{
					//wont fit in this Pkt so store and start another
					gPktList.m_u16PktLen = (( gPktList.m_u16PktLen + 15 ) & ~15 );
					u32FileLen += gPktList.m_u16PktLen;
					
					paoRetListPkts->Add( gPktList.MakeCopy() );

					gPktList.m_u16PktLen = gPktList.EmptyLen();
					s32PktIdx = 0;
					gPktList.m_u16ListCnt = 0;
				}
				//copy file length first
				*( ( __int64 *)&gPktList.m_au8List[ s32PktIdx ] ) = poInfo->m_s64FileLen;
				//copy chopped string that has display name
				memcpy( &gPktList.m_au8List[ s32PktIdx + sizeof( __int64 ) ], poInfo->m_pu8ChoppedDisplayName, u32ItemLen - sizeof( __int64 ) );

				//show trace of file we added to list
				char as8Buf[ 256 * 2 ];
				VxUnchopStr( poInfo->m_pu8ChoppedDisplayName, as8Buf );
				log_msg( 0, "Added to PktFileList file size %d file %s\n", 
						*( ( unsigned long *)&gPktList.m_au8List[ s32PktIdx ] ),
						as8Buf );


				//increment vars for next item
				s32PktIdx += u32ItemLen;
				gPktList.m_u16ListCnt++;
				gPktList.m_u16PktLen += (unsigned short)u32ItemLen;
			}	
		}
	}
	gPktList.m_u16PktLen = (( gPktList.m_u16PktLen + 15 ) & ~15 );
	paoRetListPkts->Add( gPktList.MakeCopy() );
 	return 0;
}
*/
#endif // USE_PLUGIN_FILE_SERVER